/*
 * The following code is part of https://github.com/jung-kurt/gofpdf
 *
 * Copyright (c) 2019 Arteom Korotkiy (Gmail: arteomkorotkiy)
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package font

import (
	
	
	
	
	
)

// flags
const symbolWords = 1 << 0
const symbolScale = 1 << 3
const symbolContinue = 1 << 5
const symbolAllScale = 1 << 6
const symbol2x2 = 1 << 7

type fontBoxType struct {
	Xmin, Ymin, Xmax, Ymax int
}

type utf8FontFile struct {
	fileReader           *fileReader
	LastRune             int
	tableDescriptions    map[string]*tableDescription
	outTablesData        map[string][]byte
	symbolPosition       []int
	charSymbolDictionary map[int]int
	Ascent               int
	Descent              int
	Bbox                 fontBoxType
	CapHeight            int
	StemV                int
	ItalicAngle          int
	Flags                int
	UnderlinePosition    float64
	UnderlineThickness   float64
	CharWidths           []int
	DefaultWidth         float64
	symbolData           map[int]map[string][]int
	CodeSymbolDictionary map[int]int
}

type tableDescription struct {
	name     string
	checksum []int
	position int
	size     int
}

type fileReader struct {
	readerPosition int64
	array          []byte
}

func ( *fileReader) ( int) []byte {
	 := .array[.readerPosition : .readerPosition+int64()]
	.readerPosition += int64()
	return 
}

func ( *fileReader) ( int64,  int) (int64, error) {
	if  == 0 {
		.readerPosition = 
	} else if  == 1 {
		.readerPosition += 
	} else if  == 2 {
		.readerPosition = int64(len(.array)) - 
	}
	return int64(.readerPosition), nil
}

func newUTF8Font( *fileReader) *utf8FontFile {
	 := utf8FontFile{
		fileReader: ,
	}
	return &
}

func ( *utf8FontFile) () {

	 := .readUint16()
	_ = .readUint16()
	_ = .readUint16()
	_ = .readUint16()
	.tableDescriptions = make(map[string]*tableDescription)

	for  := 0;  < ; ++ {
		 := tableDescription{
			name:     .readTableName(),
			checksum: []int{.readUint16(), .readUint16()},
			position: .readUint32(),
			size:     .readUint32(),
		}
		.tableDescriptions[.name] = &
	}
}

func ( *utf8FontFile) () string {
	return string(.fileReader.Read(4))
}

func ( *utf8FontFile) () int {
	 := .fileReader.Read(2)
	return (int([0]) << 8) + int([1])
}

func ( *utf8FontFile) () int {
	 := .fileReader.Read(4)
	return (int([0]) * 16777216) + (int([1]) << 16) + (int([2]) << 8) + int([3]) // 	16777216  = 1<<24
}

func ( *utf8FontFile) (,  []int) []int {
	 := make([]int, 2)
	if [1] > [1] {
		[1] += 1 << 16
		[0]++
	}
	[1] = [1] - [1]
	if [0] > [0] {
		[0] += 1 << 16
	}
	[0] = [0] - [0]
	[0] = [0] & 0xFFFF
	return 
}

func ( *utf8FontFile) ( []byte) []int {
	if (len() % 4) != 0 {
		for  := 0; (len() % 4) != 0; ++ {
			 = append(, 0)
		}
	}
	 := []int{0x0000, 0x0000}
	for  := 0;  < len();  += 4 {
		[0] += (int([]) << 8) + int([+1])
		[1] += (int([+2]) << 8) + int([+3])
		[0] += [1] >> 16
		[1] = [1] & 0xFFFF
		[0] = [0] & 0xFFFF
	}
	return 
}

func ( *utf8FontFile) ( int) {
	_, _ = .fileReader.seek(int64(), 0)
}

func ( *utf8FontFile) ( int) {
	_, _ = .fileReader.seek(int64(), 1)
}

// SeekTable position
func ( *utf8FontFile) ( string) int {
	return .seekTable(, 0)
}

func ( *utf8FontFile) ( string,  int) int {
	_, _ = .fileReader.seek(int64(.tableDescriptions[].position+), 0)
	return int(.fileReader.readerPosition)
}

func ( *utf8FontFile) () int16 {
	 := .fileReader.Read(2)
	 := (int16([0]) << 8) + int16([1])
	if (int() & (1 << 15)) == 0 {
		 = int16(int() - (1 << 16))
	}
	return 
}

func ( *utf8FontFile) ( int) int {
	_, _ = .fileReader.seek(int64(), 0)
	 := .fileReader.Read(2)
	return (int([0]) << 8) + int([1])
}

func ( *utf8FontFile) ( []byte,  int,  []byte) []byte {
	 = append([]byte{}, ...)
	return append(append([:], ...), [+len():]...)
}

func ( *utf8FontFile) ( []byte,  int,  int) []byte {
	 := make([]byte, 2)
	binary.BigEndian.PutUint16(, uint16())
	return .splice(, , )
}

func ( *utf8FontFile) (,  int) []byte {
	_, _ = .fileReader.seek(int64(), 0)
	if  < 1 {
		return make([]byte, 0)
	}
	 := .fileReader.Read()
	return 
}

func ( *utf8FontFile) ( string) []byte {
	 := .tableDescriptions[]
	if  == nil {
		return nil
	}
	if .size == 0 {
		return nil
	}
	_, _ = .fileReader.seek(int64(.position), 0)
	 := .fileReader.Read(.size)
	return 
}

func ( *utf8FontFile) ( string,  []byte) {
	if  == nil {
		return
	}
	if  == "head" {
		 = .splice(, 8, []byte{0, 0, 0, 0})
	}
	.outTablesData[] = 
}

func ( *utf8FontFile) () map[int][]int {
	 := .SeekTable("cmap")
	.skip(2)
	 := .readUint16()
	 := 0
	for  := 0;  < ; ++ {
		 := .readUint16()
		 := .readUint16()
		 := .readUint32()
		 := .fileReader.readerPosition
		if ( == 3 &&  == 1) ||  == 0 {
			 := .getUint16( + )
			if  == 4 {
				 =  + 
				break
			}
		}
		.seek(int())
	}

	if  == 0 {
		fmt.Printf("Font does not have cmap for Unicode\n")
		return nil
	}

	 := make(map[int][]int)
	 := make(map[int]int)
	.generateSCCSDictionaries(, , )

	.charSymbolDictionary = 

	return 
}

func ( *utf8FontFile) ( map[int]int) (map[int]int, map[int]int, map[int]int, []int) {
	 := map[int]int{0: 0}
	 := make(map[int]int)
	for ,  := range  {
		if ,  := .charSymbolDictionary[];  {
			[.charSymbolDictionary[]] = 
			[] = .charSymbolDictionary[]

		}
		.LastRune = max(.LastRune, )
	}

	 := .tableDescriptions["glyf"].position

	 := make(map[int]int)
	 := keySortInt()

	 := 0
	 := 0
	for ,  := range  {
		 = max(, [])
		[] = 
		++
	}
	 := keySortInt()
	 := make(map[int]int)
	for ,  := range  {
		[] = [[]]
	}
	.CodeSymbolDictionary = 

	 = keySortInt()
	for ,  := range  {
		_, , ,  = .getSymbols(, &, , , )
	}

	return , , , 
}

func ( *utf8FontFile) ( map[int]int,  int) []byte {
	 := keySortInt()
	 := 0
	 := make(map[int][]int)
	 := -2
	 := -1
	for ,  := range  {
		if  == (+1) && [] == (+1) {
			if ,  := []; ! ||  == nil {
				[] = make([]int, 0)
			}
			[] = append([], [])
		} else {
			 = 
			[] = make([]int, 0)
			[] = append([], [])
		}
		 = 
		 = []
	}
	 := keySortArrayRangeMap()
	 := len() + 1

	 := 1
	 := 0
	for *2 <=  {
		 =  * 2
		 =  + 1
	}
	 =  * 2
	 := *2 - 

	 := []int{0, 1, 3, 1, 0, 12, 4}

	 := []int{ * 2, , , }
	for ,  := range  {
		 :=  + (len([]) - 1)
		 = append(, )
	}
	 = append(, 0xFFFF)
	 = append(, 0)

	 = append(, ...)
	 = append(, 0xFFFF)
	for ,  := range  {
		 := -( - [][0])
		 = append(, )
	}
	 = append(, 1)
	for range  {
		 = append(, 0)

	}
	 = append(, 0)
	for ,  := range  {
		 = append(, []...)
	}
	 = append(, 0)

	// Calculating cmap length based off of fpdf https://github.com/Setasign/FPDF/blob/f4104a04c9a3f95c4c26a0a0531abebcc980987a/makefont/ttfparser.php#L476
	// https://learn.microsoft.com/en-us/typography/opentype/spec/cmap#format-4-segment-mapping-to-delta-values
	// The 3 extra bytes are for encodingID, offset, format, which are also part of the cmap
	 := (3 + len()) * 2
	 = append(, , 0)
	 = append(, ...)

	 := make([]byte, 0)
	for ,  := range  {
		 = append(, packUint16()...)
	}

	return 
}

// GenerateCutFont fill utf8FontFile from .utf file, only with runes from usedRunes
func ( *utf8FontFile) ( map[int]int) []byte {
	.fileReader.readerPosition = 0
	.symbolPosition = make([]int, 0)
	.charSymbolDictionary = make(map[int]int)
	.tableDescriptions = make(map[string]*tableDescription)
	.outTablesData = make(map[string][]byte)
	.Ascent = 0
	.Descent = 0
	.skip(4)
	.LastRune = 0
	.generateTableDescriptions()

	.SeekTable("head")
	.skip(50)
	 := .readUint16()

	.SeekTable("hhea")
	.skip(34)
	 := .readUint16()
	 := 

	.SeekTable("maxp")
	.skip(4)
	 := .readUint16()

	 := .generateCMAP()
	if  == nil {
		return nil
	}

	.parseHMTXTable(, , , 1.0)

	.parseLOCATable(, )

	, , ,  := .parseSymbols()

	 = len()
	 = 

	.setOutTable("name", .getTableData("name"))
	.setOutTable("cvt ", .getTableData("cvt "))
	.setOutTable("fpgm", .getTableData("fpgm"))
	.setOutTable("prep", .getTableData("prep"))
	.setOutTable("gasp", .getTableData("gasp"))

	 := .getTableData("post")
	 = append(append([]byte{0x00, 0x03, 0x00, 0x00}, [4:16]...), []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}...)
	.setOutTable("post", )

	delete(, 0)

	.setOutTable("cmap", .generateCMAPTable(, ))

	 := .getTableData("glyf")

	 := make([]int, 0)
	 := make([]byte, 0)
	 := 0
	 := make([]byte, 0)
	.symbolData = make(map[int]map[string][]int, 0)

	for ,  := range  {
		 := .getMetrics(, )
		 = append(, ...)

		 = append(, )
		 := .symbolPosition[]
		 := .symbolPosition[+1] - 
		 := [ : +]
		var  int
		if  > 0 {
			 = unpackUint16([0:2])
		}

		if  > 2 && (&(1<<15)) != 0 {
			 := 10
			 := symbolContinue
			 := 0
			for ( & symbolContinue) != 0 {
				++
				 = unpackUint16([ : +2])
				 = 
				 = unpackUint16([+2 : +4])
				 := 
				if ,  := .symbolData[]; ! {
					.symbolData[] = make(map[string][]int)
				}
				if ,  := .symbolData[]["compSymbols"]; ! {
					.symbolData[]["compSymbols"] = make([]int, 0)
				}
				.symbolData[]["compSymbols"] = append(.symbolData[]["compSymbols"], )
				 = .insertUint16(, +2, [])
				 += 4
				if ( & symbolWords) != 0 {
					 += 4
				} else {
					 += 2
				}
				if ( & symbolScale) != 0 {
					 += 2
				} else if ( & symbolAllScale) != 0 {
					 += 4
				} else if ( & symbol2x2) != 0 {
					 += 8
				}
			}
		}

		 = append(, ...)
		 += 
		if %4 != 0 {
			 := 4 - ( % 4)
			 = append(, make([]byte, )...)
			 += 
		}
	}

	 = append(, )
	.setOutTable("glyf", )

	.setOutTable("hmtx", )

	 := make([]byte, 0)
	if (( + 1) >> 1) > 0xFFFF {
		 = 1
		for ,  := range  {
			 = append(, packUint32()...)
		}
	} else {
		 = 0
		for ,  := range  {
			 = append(, packUint16(/2)...)
		}
	}
	.setOutTable("loca", )

	 := .getTableData("head")
	 = .insertUint16(, 50, )
	.setOutTable("head", )

	 := .getTableData("hhea")
	 = .insertUint16(, 34, )
	.setOutTable("hhea", )

	 := .getTableData("maxp")
	 = .insertUint16(, 4, )
	.setOutTable("maxp", )

	 := .getTableData("OS/2")
	.setOutTable("OS/2", )

	return .assembleTables()
}

func ( *utf8FontFile) ( int,  *int,  map[int]int,  map[int]int,  []int) (*int, map[int]int, map[int]int, []int) {
	 := .symbolPosition[]
	 := .symbolPosition[+1] - 
	if  == 0 {
		return , , , 
	}
	.seek(* + )

	 := .readInt16()

	if  < 0 {
		.skip(8)
		 := symbolContinue
		for &symbolContinue != 0 {
			 = .readUint16()
			 := .readUint16()
			if ,  := []; ! {
				[] = len()
				[] = 1
				 = append(, )
			}
			,  := .fileReader.seek(0, 1)
			_, _, _,  = .(, , , , )
			.seek(int())
			if &symbolWords != 0 {
				.skip(4)
			} else {
				.skip(2)
			}
			if &symbolScale != 0 {
				.skip(2)
			} else if &symbolAllScale != 0 {
				.skip(4)
			} else if &symbol2x2 != 0 {
				.skip(8)
			}
		}
	}
	return , , , 
}

func ( *utf8FontFile) (,  int,  map[int][]int,  float64) {
	var  int
	 := .SeekTable("hmtx")
	 := 0
	var  []int
	.CharWidths = make([]int, 256*256)
	 := 0
	 = unpackUint16Array(.getRange(, *4))
	for  := 0;  < ; ++ {
		 = [(*2)+1]
		if ,  := [];  ||  == 0 {

			if  >= (1 << 15) {
				 = 0
			}
			if  == 0 {
				.DefaultWidth =  * float64()
				continue
			}
			for ,  := range [] {
				if  != 0 &&  != 65535 {
					 = int(math.Round( * float64()))
					if  == 0 {
						 = 65535
					}
					if  < 196608 {
						.CharWidths[] = 
						++
					}
				}
			}
		}
	}
	 :=  - 
	for  := 0;  < ; ++ {
		 :=  + 
		if ,  := [];  {
			for ,  := range [] {
				if  != 0 &&  != 65535 {
					 = int(math.Round( * float64()))
					if  == 0 {
						 = 65535
					}
					if  < 196608 {
						.CharWidths[] = 
						++
					}
				}
			}
		}
	}
	.CharWidths[0] = 
}

func ( *utf8FontFile) (,  int) []byte {
	 := .SeekTable("hmtx")
	var  []byte
	if  <  {
		.seek( + ( * 4))
		 = .fileReader.Read(4)
	} else {
		.seek( + (( - 1) * 4))
		 = .fileReader.Read(2)
		.seek( + ( * 2) + ( * 2))
		 = append(, .fileReader.Read(2)...)
	}
	return 
}

func ( *utf8FontFile) (,  int) {
	 := .SeekTable("loca")
	.symbolPosition = make([]int, 0)
	if  == 0 {
		 := .getRange(, (*2)+2)
		 := unpackUint16Array()
		for  := 0;  <= ; ++ {
			.symbolPosition = append(.symbolPosition, [+1]*2)
		}
	} else if  == 1 {
		 := .getRange(, (*4)+4)
		 := unpackUint32Array()
		for  := 0;  <= ; ++ {
			.symbolPosition = append(.symbolPosition, [+1])
		}
	} else {
		fmt.Printf("Unknown loca table format %d\n", )
		return
	}
}

func ( *utf8FontFile) ( int,  map[int][]int,  map[int]int) {
	 := 0
	.seek( + 2)
	 := .readUint16()
	 :=  + 
	.skip(2)

	 := .readUint16() / 2
	.skip(6)
	 := make([]int, 0)
	for  := 0;  < ; ++ {
		 = append(, .readUint16())
	}
	.skip(2)
	 := make([]int, 0)
	for  := 0;  < ; ++ {
		 = append(, .readUint16())
	}
	 := make([]int, 0)
	for  := 0;  < ; ++ {
		 = append(, int(.readInt16()))
	}
	 := .fileReader.readerPosition
	 := make([]int, 0)
	for  := 0;  < ; ++ {
		 = append(, .readUint16())
	}
	var  int
	for  := 0;  < ; ++ {
		 := [] + 1
		for  := [];  < ; ++ {
			if [] == 0 {
				 = ( + []) & 0xFFFF
			} else {
				 := (-[])*2 + []
				 = int() + 2* + 
				if  >=  {
					 = 0
				} else {
					 = .getUint16()
					if  != 0 {
						 = ( + []) & 0xFFFF
					}
				}
			}
			[] = 
			if  < 196608 {
				 = max(, )
			}
			[] = append([], )
		}
	}
}

func max(,  int) int {
	if  >  {
		return 
	}
	return 
}

func ( *utf8FontFile) () []byte {
	 := make([]byte, 0)
	 := len(.outTablesData)
	 := 1
	 := 0
	for *2 <=  {
		 =  * 2
		 =  + 1
	}
	 =  * 16
	 := *16 - 

	 = append(, packHeader(0x00010000, , , , )...)

	 := .outTablesData
	 := keySortStrings()

	 := 12 + *16
	 := 0

	for ,  := range  {
		if  == "head" {
			 = 
		}
		 = append(, []byte()...)
		 := .generateChecksum([])
		 = append(, pack2Uint16([0], [1])...)
		 = append(, pack2Uint32(, len([]))...)
		 := (len([]) + 3) &^ 3
		 =  + 
	}

	for ,  := range  {
		 := append([]byte{}, []...)
		 = append(, []byte{0, 0, 0}...)
		 = append(, [:(len()&^3)]...)
	}

	 := .generateChecksum([]byte())
	 = .calcInt32([]int{0xB1B0, 0xAFBA}, )
	 = .splice(, ( + 8), pack2Uint16([0], [1]))
	return 
}

func unpackUint16Array( []byte) []int {
	 := make([]int, 1)
	 := bytes.NewReader()
	 := make([]byte, 2)
	var  error
	var  int
	,  = .Read()
	for  == nil &&  > 0 {
		 = append(, int(binary.BigEndian.Uint16()))
		,  = .Read()
	}
	return 
}

func unpackUint32Array( []byte) []int {
	 := make([]int, 1)
	 := bytes.NewReader()
	 := make([]byte, 4)
	var  error
	var  int
	,  = .Read()
	for  == nil &&  > 0 {
		 = append(, int(binary.BigEndian.Uint32()))
		,  = .Read()
	}
	return 
}

func unpackUint16( []byte) int {
	return int(binary.BigEndian.Uint16())
}

func packHeader( uint32, , , ,  int) []byte {
	 := make([]byte, 0)
	 := make([]byte, 4)
	binary.BigEndian.PutUint32(, )
	 = append(, ...)
	 := make([]byte, 2)
	binary.BigEndian.PutUint16(, uint16())
	 = append(, ...)
	binary.BigEndian.PutUint16(, uint16())
	 = append(, ...)
	binary.BigEndian.PutUint16(, uint16())
	 = append(, ...)
	binary.BigEndian.PutUint16(, uint16())
	 = append(, ...)
	return 
}

func pack2Uint16(,  int) []byte {
	 := make([]byte, 0)
	 := make([]byte, 2)
	binary.BigEndian.PutUint16(, uint16())
	 = append(, ...)
	binary.BigEndian.PutUint16(, uint16())
	 = append(, ...)
	return 
}

func pack2Uint32(,  int) []byte {
	 := make([]byte, 0)
	 := make([]byte, 4)
	binary.BigEndian.PutUint32(, uint32())
	 = append(, ...)
	binary.BigEndian.PutUint32(, uint32())
	 = append(, ...)
	return 
}

func packUint32( int) []byte {
	 := make([]byte, 4)
	binary.BigEndian.PutUint32(, uint32())
	return 
}

func packUint16( int) []byte {
	 := make([]byte, 2)
	binary.BigEndian.PutUint16(, uint16())
	return 
}

func keySortStrings( map[string][]byte) []string {
	 := make([]string, len())
	 := 0
	for  := range  {
		[] = 
		++
	}
	sort.Strings()
	return 
}

func keySortInt( map[int]int) []int {
	 := make([]int, len())
	 := 0
	for  := range  {
		[] = 
		++
	}
	sort.Ints()
	return 
}

func keySortArrayRangeMap( map[int][]int) []int {
	 := make([]int, len())
	 := 0
	for  := range  {
		[] = 
		++
	}
	sort.Ints()
	return 
}

// UTF8CutFont is a utility function that generates a TrueType font composed
// only of the runes included in cutset. The rune glyphs are copied from This
// function is demonstrated in ExampleUTF8CutFont().
func ( []byte,  string) ( []byte) {
	 := newUTF8Font(&fileReader{readerPosition: 0, array: })
	 := map[int]int{}
	for ,  := range  {
		[] = int()
	}
	 = .GenerateCutFont()
	return
}